/*
 * Copyright ©2015-2021 Jaemon. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.gitee.jaemon.spring.adorner.generator;

import io.gitee.jaemon.spring.adorner.AdornerConstants;
import io.gitee.jaemon.spring.adorner.core.*;
import io.gitee.jaemon.spring.adorner.exception.AdornerException;
import io.gitee.jaemon.spring.adorner.type.ApiDefinition;
import io.gitee.jaemon.spring.adorner.type.CommonParameter;
import io.gitee.jaemon.spring.adorner.type.ParamPair;
import io.gitee.jaemon.spring.adorner.util.*;

import java.io.IOException;
import java.lang.reflect.Method;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;

import static io.gitee.jaemon.spring.adorner.AdornerConstants.*;
import static io.gitee.jaemon.spring.adorner.core.AdornerProperty.INSTANCE;
import static io.gitee.jaemon.spring.adorner.util.AdornerUtils.paramTable;
import static io.gitee.jaemon.spring.adorner.util.MarkdownUtils.*;

/**
 * markdown文档生成器
 *
 * @author Jaemon
 * @since 1.0
 */
public class MarkdownGenerator implements DocGenerator<ParamPair> {
    @Override
    public void generate(List<ApiDefinition<ParamPair>> apiDefinitions, AdornerApplicationContext context) throws AdornerException {
        StringBuilder result = new StringBuilder();

        String head = head(apiDefinitions, context);
        if (StringUtils.isNotEmpty(head)) {
            result.append(head).append(BRANK_LINE).append(THREE_BREAK);
        }
        String body = body(apiDefinitions, context);
        if (StringUtils.isNotEmpty(body)) {
            result.append(body).append(BRANK_LINE).append(THREE_BREAK);
        }
        String foot = foot(apiDefinitions, context);
        if (StringUtils.isNotEmpty(foot)) {
            result.append(foot).append(BRANK_LINE).append(THREE_BREAK);
        }

        String content = result.toString();
        if (content.trim().length() == 0) {
            throw new AdornerException("内容为空, 生成文件失败");
        }

        try {
            FileUtils.write(content, context.filePath());
        } catch (IOException e) {
            throw new AdornerException("文件=[%s]生成失败", context.filePath());
        }
    }

    /**
     * 文档头部内容
     *
     * @param apiDefinitions
     *      接口定义集合 {@link ApiDefinition}
     * @param context
     *      上下文对象
     * @throws AdornerException
     *      ex {@link AdornerException}
     * @return
     *      文档头部内容
     */
    protected String head(List<ApiDefinition<ParamPair>> apiDefinitions, AdornerApplicationContext context) throws AdornerException {
        StringBuilder head = new StringBuilder();
        head.append("[TOC]").append(THREE_BREAK);

        head.append(ONE_LEVEL_TITLE_TYPE).append("版本修订记录").append(LINE_BREAK);
        head.append("| 版本 | 作者 | 日期 | 备注 |").append(NEWLINE);
        head.append("|:---:|:---:|:---:|:---:|").append(NEWLINE);
        head.append("| 1.0.0 | Adorner | ").append(
                LocalDate.now().format(AdornerConstants.DATE_FORMAT)
        ).append(" | Initialization Version |").append(NEWLINE);

        CommonParameter commonParameter = context.commonParameter();
        if (commonParameter != null) {
            Class<?> request = commonParameter.request();
            String requestTable = paramTable(request);
            if (StringUtils.isNotEmpty(requestTable)) {
                head.append(BRANK_LINE).append(THREE_BREAK);
                head.append(ONE_LEVEL_TITLE_TYPE).append("公共请求体").append(LINE_BREAK);
                head.append(requestTable);
            }

            Class<?> response = commonParameter.response();
            String responseTable = paramTable(response);
            if (StringUtils.isNotEmpty(responseTable)) {
                head.append(BRANK_LINE).append(THREE_BREAK);
                head.append(ONE_LEVEL_TITLE_TYPE).append("公共响应体").append(LINE_BREAK);
                head.append(responseTable);
            }
        }

        return head.toString();
    }

    /**
     * 文档主体内容
     *
     * @param apiDefinitions
     *      接口定义集合 {@link ApiDefinition}
     * @param context
     *      上下文对象
     * @throws AdornerException
     *      ex {@link AdornerException}
     * @return
     *      文档主体内容
     */
    protected String body(List<ApiDefinition<ParamPair>> apiDefinitions, AdornerApplicationContext context) throws AdornerException {
        StringBuilder body = new StringBuilder();

        body.append(ONE_LEVEL_TITLE_TYPE).append("API定义").append(LINE_BREAK);
        Map<String, List<ApiDefinition<ParamPair>>> apiDefinitionsGroup =
                apiDefinitions.stream().collect(
                        Collectors.groupingBy(e -> e.getGroupName(), LinkedHashMap::new, Collectors.toList())
                );
        String titleType = context.group() ? THREE_LEVEL_TITLE_TYPE : TWO_LEVEL_TITLE_TYPE;

        IndexConvert convert = context.indexConvert();
        int groupIndex = 0, apiIndex = 0;
        for (
                Map.Entry<String, List<ApiDefinition<ParamPair>>> ads : apiDefinitionsGroup.entrySet()
        ) {
            if (context.group()) {
                groupIndex++;
                body.append(TWO_LEVEL_TITLE_TYPE).append(convert.group(groupIndex)).append(ads.getKey()).append(LINE_BREAK);
            }

            List<ApiDefinition<ParamPair>> apis = ads.getValue();
            for (
                    int i = 0; i < apis.size(); i++
            ) {
                ApiDefinition<ParamPair> apiDefinition = apis.get(i);
                apiIndex++;
                int index = context.group()? i + 1 : apiIndex;

                body.append(titleType)
                        .append(convert.api(groupIndex, index))
                        .append(
                                apiName(
                                        apiDefinition.getClazz(), apiDefinition.getMethod(), apiDefinition.getApiName()
                                )
                        )
                        .append(LINE_BREAK);

                if (context.simplify() || apiDefinition.getUrls().length == 1) {
                    body.append(MARKDOWN_ITEM).append(INSTANCE.get(SPRING_ADORNER_API_REQUEST_URL))
                            .append(COLON).append(apiDefinition.getDefaultUrl()).append(LINE_BREAK);
                } else {
                    body.append(MARKDOWN_ITEM).append(INSTANCE.get(SPRING_ADORNER_API_REQUEST_URL)).append(LINE_BREAK);

                    for (String url : apiDefinition.getUrls()) {
                        body.append("  ").append(MARKDOWN_ITEM).append(url).append(LINE_BREAK);
                    }
                }

                String requestMethod;
                if (context.simplify()) {
                    requestMethod = apiDefinition.getDefaultRequestMethod().name();
                } else {
                    requestMethod = Arrays.stream(apiDefinition.getRequestMethods()).map(e -> e.name()).collect(Collectors.joining(COMMA));
                }
                body.append(MARKDOWN_ITEM).append(INSTANCE.get(SPRING_ADORNER_API_REQUEST_METHOD))
                        .append(COLON).append(requestMethod).append(LINE_BREAK);

                ApiParamShow apiParamShow = context.apiParamShow();
                // 请求参数展示
                String request = apiParamShow.request(apiDefinition.getRequest());
                if (StringUtils.isNotEmpty(request)) {
                    body.append(request);
                }

                // 响应参数展示
                String response = apiParamShow.response(apiDefinition.getResponse());
                if (StringUtils.isNotEmpty(response)) {
                    body.append(response);
                }

                if (!context.group() || i != apis.size() - 1) {
                    body.append(BRANK_LINE).append(LINE_BREAK);
                }

                LoggerHelper.debug("Generate API=[%s.%s()] interface doc content.",
                        apiDefinition.getClazz().getName(), apiDefinition.getMethod().getName());
            }

            if (context.group()) {
                body.append(BRANK_LINE).append(LINE_BREAK);
            }
        }

        return body.toString();
    }

    public String apiName(Class clazz, Method method, String apiName) {
        boolean exists =
                clazz.isAnnotationPresent(Deprecated.class) ||
                        method.isAnnotationPresent(Deprecated.class);

        return exists ? String.format("~~%s~~", apiName) : apiName;
    }

    /**
     * 文档尾内容
     *
     * @param apiDefinitions
     *      接口定义集合 {@link ApiDefinition}
     * @param context
     *      上下文对象
     * @throws AdornerException
     *      ex {@link AdornerException}
     * @return
     *      文档尾内容
     */
    protected String foot(List<ApiDefinition<ParamPair>> apiDefinitions, AdornerApplicationContext context) throws AdornerException {
        StringBuilder foot = new StringBuilder();

        return foot.toString();
    }

}